/**
 * 
 */
package com.cmcc.awareness;

import java.util.Arrays;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.pcap4j.packet.Packet;

import com.cmcc.awareness.util.CommonUtils;

/**
 * @author Amish
 * @date 2018年12月7日 下午7:11:37
 * 
 * 报文中字节与负载长度关系表达式：d
 * 
 * eg:
    {d|0|0x110a}
	{d|14|0x200}
	{d|12|0x0406}
 */
public class ProtoDatProtocolFeatureCodeElement_d implements ProtoDatProtocolFeatureCodeElement {
	private static Logger logger = LogManager.getLogger(ProtoDatProtocolFeatureCodeElement_d.class);

	String initialStr;
	
	String offset;//字节开始的偏移位置,不为负，且示例里没有负
	String endian = "0";//当为0，即主机字节序时，可以省略
	                    //主机字节序/小端字节序/LE/little-endian/：低序字节存储在起始位置（起始位置内存地址小）
	                    //网络字节序/大端字节序/BE/big-endian/：所见即所得-直观，高序字节存储在起始位置（起始位置内存地址小），即可理解为先发送高位字节。
	String byteNum; //取值范围1、2、4
	String plusNum;
	
	public String appName;
	public String protocolType;
	public ProtoDatProtocolFeatureCodeElement_d(String appName,String protocolType){
		this.appName = appName;
		this.protocolType = protocolType;
	}
	
	@Override
	public String getType() {
		return "d";
	}

	@Override
	public boolean distinguish(String protocolType,jpcap.packet.Packet packet) {
		
		try{
			byte[] data = packet.data;
		    int offsetInt = Integer.valueOf(offset);
			int byteNumInt = Integer.valueOf(byteNum);
			if(data ==null || data.length == 0 )
				return false;
			
			if(offsetInt>=0 && data.length < (offsetInt + byteNumInt) )
				return false;
						
		    if(offsetInt < 0 && data.length < Math.abs(offsetInt))//此处理论上没有offset为负的情况
		    	return false;
		    
			int packetDataLen = data.length;
			byte[] lenByte = Arrays.copyOfRange(data, offsetInt, offsetInt+byteNumInt);
			if(endian.equals("0")){
				lenByte = CommonUtils.reverseBytes(lenByte);
			}
			
			if(byteNumInt == 4){//如果4个字节，这个数远大于一个packet的MTU(1500),TODO 暂时处理成最高1字节都为0等情况,否则最高位二进制1时，CommonUtils.bytesToInt(lenByte);会报错
				lenByte[0] = (byte) 0x00;
			}
			
			int lenByteInt = CommonUtils.bytesToInt(lenByte);
			int plusNumInt = Integer.parseInt(plusNum, 16);
			
			if( (lenByteInt + plusNumInt) == packetDataLen ){
				return true;
			}
			return false;
		}catch(Exception e){
			e.printStackTrace();
			System.out.println("Distinguish Exception -- <"+this.appName+">"+"<"+this.protocolType+"> : "+this.toString());
			return false;
		}
	}


	@Override
	public void parseSelf(String element) {
		
		try{			
			this.initialStr = element;
			String[] str = element.split("\\|");
	        this.offset = str[1];
	        str[2] = str[2].replaceAll("0x", "");
	        if(str[2].length() == 3){
	        	this.endian = "0";
	        	this.byteNum = str[2].substring(0,1);
	        	this.plusNum = str[2].substring(1);
	        }else{
	        	this.endian = str[2].substring(0,1);
	        	this.byteNum = str[2].substring(1,2);
	        	this.plusNum = str[2].substring(2);
	        }

		}catch(Exception e){
			e.printStackTrace();
		}

		
	}
	
	public String toString(){
		return "{"+initialStr+"}";
	}

	@Override
	public boolean distinguish(String protocolType, Packet packet) {
		try{
			byte[] data = packet.getRawData();
		    int offsetInt = Integer.valueOf(offset);
			int byteNumInt = Integer.valueOf(byteNum);
			if(data ==null || data.length == 0 )
				return false;
			
			if(offsetInt>=0 && data.length < (offsetInt + byteNumInt) )
				return false;
						
		    if(offsetInt < 0 && data.length < Math.abs(offsetInt))//此处理论上没有offset为负的情况
		    	return false;
		    
			int packetDataLen = data.length;
			byte[] lenByte = Arrays.copyOfRange(data, offsetInt, offsetInt+byteNumInt);
			if(endian.equals("0")){
				lenByte = CommonUtils.reverseBytes(lenByte);
			}
			
			if(byteNumInt == 4){//如果4个字节，这个数远大于一个packet的MTU(1500),TODO 暂时处理成最高1字节都为0等情况,否则最高位二进制1时，CommonUtils.bytesToInt(lenByte);会报错
				lenByte[0] = (byte) 0x00;
			}
			
			int lenByteInt = CommonUtils.bytesToInt(lenByte);
			int plusNumInt = Integer.parseInt(plusNum, 16);
			
			if( (lenByteInt + plusNumInt) == packetDataLen ){
				return true;
			}
			return false;
		}catch(Exception e){
			e.printStackTrace();
			System.out.println("Distinguish Exception -- <"+this.appName+">"+"<"+this.protocolType+"> : "+this.toString());
			return false;
		}
	}

}
